package com.astersoft.lib.util;

/*
 * Copyright 2012 AsterSoft personal developer - Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.channels.FileChannel;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
 * 
 * @author dario.durzo
 * @version 0.1
 * @created.date 2009.08.26
 */
public class ASFileUtils {
	public static final int BUFFER = 4096;
	public static enum Sizes {MB, KB, BYTE};

	/**
	 * Internal utility class
	 * @author Dario
	 *
	 */
	private class Filter
	implements FilenameFilter {
		public boolean accept(File dir, String name) {
			return true;
		}        
	}

	/**
	 * List file inside folder recursively
	 * 
	 * @param startDir
	 * @param filter
	 * @return
	 * @throws FileNotFoundException
	 */
	public static List<File> recursiveListFiles(File startDir, FileFilter filter)
			throws FileNotFoundException {
		List<File> result = new ArrayList<File>();
		File[] filesAndDirs = startDir.listFiles();
		List<File> filesDirs = Arrays.asList(filesAndDirs);
		for(File file : filesDirs){
			if(filter.accept(file))
				result.add(file);

			if (!file.isFile()) {
				//must be a directory
				//recursive call!
				List<File> deeperList = recursiveListFiles(file, filter);
				result.addAll(deeperList);
			}
		}
		return result;
	}

	/**
	 * Get the File Formatted real Size
	 * 
	 * @param f
	 * @param format
	 * @param decimalPos
	 * @return
	 */
	public static Double getFMTSize(File f, Sizes format, int decimalPos){
		NumberFormat fmt = NumberFormat.getNumberInstance(Locale.ENGLISH);
		if (decimalPos >= 0) {
			fmt.setMaximumFractionDigits(decimalPos);
		}
		final double size = f.length();
		double val;
		switch(format){
		case MB:
			val = size / (1024 * 1024);
			return new Double(fmt.format(val));
		case KB:
		case BYTE:
			val = size / 1024;
			return new Double(fmt.format(val));
		}
		return size;
	}

	/**
	 * Get the postFix File Name of file
	 * 
	 * @param file
	 * @param postFix
	 * @return
	 */
	public static String postFixFileName(File file, String postFix){
		String extension = file.getName().substring(file.getName().lastIndexOf("."));
		String name = file.getName().substring(0, file.getName().lastIndexOf("."));
		String path = file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - file.getName().length());
		return path + name + postFix + extension;
	}

	/**
	 * Get the formatted size of file
	 * 
	 * @param f
	 * @param decimalPos
	 * @return
	 */
	public static String getFormattedSize(File f, int decimalPos) {
		NumberFormat fmt = NumberFormat.getNumberInstance();
		if (decimalPos >= 0) {
			fmt.setMaximumFractionDigits(decimalPos);
		}
		final double size = f.length();
		double val = size / (1024 * 1024);
		if (val > 1){
			return fmt.format(val).concat(" MB");
		}
		val = size / 1024;
		if (val > 10) {
			return fmt.format(val).concat(" KB");
		}
		return fmt.format(val).concat(" bytes");
	}

	/**
	 * Read file as a String
	 * 
	 * @param filePath
	 * @return
	 * @throws java.io.IOException
	 */
	public static String readFileAsString(String filePath)
			throws java.io.IOException{
		File f = new File(filePath);
		return readFileAsString(f);
	}

	public static String readFileAsString(File inputFile)
			throws java.io.IOException {
		StringBuffer fileData = new StringBuffer(1000);
		BufferedReader reader = new BufferedReader(new FileReader(inputFile));
		char[] buf = new char[1024];
		int numRead=0;
		while((numRead=reader.read(buf)) != -1){
			String readData = String.valueOf(buf, 0, numRead);
			fileData.append(readData);
			buf = new char[1024];
		}
		reader.close();
		return fileData.toString();
	}

	/**
	 * Convert String to InputStream
	 * 
	 * @param s
	 * @return
	 */
	public static InputStream stringToInputStream(String s)
			throws UnsupportedEncodingException { return stringToInputStream(s, null); }
	public static InputStream stringToInputStream(String s, String charset) 
			throws UnsupportedEncodingException {
		if(charset == null || charset.equals("")) charset = "UTF-8";
		try {
			return new ByteArrayInputStream(s.getBytes(charset));
		} catch (UnsupportedEncodingException e) {
			throw e;
		}
	}

	/**
	 * Write file to stream
	 * without need to specify file name.
	 * 
	 * @param is
	 * @param os
	 * @param mod
	 */
	public static void writeToStream(InputStream is, OutputStream os, boolean mod) {
		try {
			byte buffer[] = new byte[BUFFER];
			int byt;
			while((byt = is.read(buffer)) >= 0) 
				os.write(buffer, 0, byt);
			if(mod)
				is.close();
		} catch(IOException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * Write byte array to file
	 * 
	 * @param bytes
	 * @param filePath
	 */
	public static void writeByteArrayToFile(byte[] bytes, String filePath) 
			throws Exception {
		try
		{
			FileOutputStream fos = new FileOutputStream(filePath);
			fos.write(bytes);
			fos.close();

		} catch(FileNotFoundException ex) {
			System.out.println("FileNotFoundException : " + ex);
			throw ex;
		} catch(IOException ioe) {
			System.out.println("IOException : " + ioe);
			throw ioe;
		}
	}

	/**
	 * Copy files
	 * 
	 * @param source
	 * @param target
	 * @return
	 */
	public static boolean copy(String source, String target) {
		try {
			File f1 = new File(source);
			File f2 = new File(target);
			FileInputStream input = new FileInputStream(f1);
			FileOutputStream output = new FileOutputStream(f2);
			FileChannel c1 = input.getChannel();
			FileChannel c2 = output.getChannel();

			byte[] buffer = new byte[(int)c1.size()];

			input.read(buffer);
			output.write(buffer);

			c1.close();
			c2.close();

			input.close();
			output.close();   

			return true;
		} catch (Exception ex) {
			ex.printStackTrace();
			return false;
		}        
	}

	/**
	 * Return folder list inside path
	 * (recursive)
	 * 
	 * @param path
	 * @return
	 */
	public String[] getFolders(String path) {
		ArrayList<String> folders = new ArrayList<String>();

		File f = new File(path);
		FilenameFilter filter = new Filter();
		File[] files = f.listFiles(filter);

		if(files == null)       
			return null;

		for(int index = 0; index < files.length; index++)
			if(files[index].isDirectory())
				folders.add(files[index].getName());

		String[] tmp = new String[folders.size()];
		return (String[])folders.toArray(tmp);
	}

	/**
	 * Get the file Extension
	 * 
	 * @param file
	 * @return
	 */
	public static String getExtension(File file) {
		return getExtension(file.getName());
	}

	public static String getExtension(String fileName) {
		if (null == fileName) throw new IllegalArgumentException("fileName can't be null.");

		String	ext = null;

		int	index = fileName.lastIndexOf('.');
		if (index > 0 &&  index < fileName.length() - 1) {
			ext = fileName.substring(index+1).toLowerCase();
		}

		return ext;
	}

	/**
	 * Get filename without extension
	 * 
	 * @param fileName
	 * @return
	 */
	public static String getFileNameWithoutExtension(File file) {
		return getFileNameWithoutExtension(file.getName());
	}
	public static String getFileNameWithoutExtension(String fileName) {
		File tmpFile = new File(fileName);
		tmpFile.getName();
		int whereDot = tmpFile.getName().lastIndexOf('.');
		if (0 < whereDot && whereDot <= tmpFile.getName().length() - 2 ) {
			return tmpFile.getName().substring(0, whereDot);
		}    
		return "";
	}

	/**
	 * Combine multiple paths together
	 * 
	 * @param paths
	 * @return
	 */
	public static String concatPaths(String... paths){ return combinePaths(paths); }
	public static String combinePaths(String... paths){
		File file = new File(paths[0]);

		for (int i = 1; i < paths.length ; i++) {
			file = new File(file, paths[i]);
		}

		return file.getPath();
	}

	/**
	 * Delete specified line from file
	 * 
	 * @param file
	 * @param lineToRemove
	 */
	public static void removeLineFromFile(String file, String lineToRemove) {
		try {
			File inFile = new File(file);

			if (!inFile.isFile()) {
				System.out.println("Parameter is not an existing file");
				return;
			}

			//Construct the new file that will later be renamed to the original filename. 
			File tempFile = new File(inFile.getAbsolutePath() + ".tmp");

			BufferedReader br = new BufferedReader(new FileReader(file));
			PrintWriter pw = new PrintWriter(new FileWriter(tempFile));

			String line = null;

			//Read from the original file and write to the new 
			//unless content matches data to be removed.
			while ((line = br.readLine()) != null) {

				if (!line.trim().equals(lineToRemove)) {

					pw.println(line);
					pw.flush();
				}
			}
			pw.close();
			br.close();

			//Delete the original file
			if (!inFile.delete()) {
				System.out.println("Could not delete file");
				return;
			} 

			//Rename the new file to the filename the original file had.
			if (!tempFile.renameTo(inFile))
				System.out.println("Could not rename file");

		} catch (FileNotFoundException ex) {
			ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * By default File#delete fails for non-empty directories, it works like "rm". 
	 * We need something a little more brutual - this does the equivalent of "rm -r"
	 * @param path Root File Path
	 * @return true iff the file and all sub files/directories have been removed
	 * 
	 * @param folder
	 * @return
	 * @throws FileNotFoundException
	 */
	public static boolean deleteFolder(File folder)
	throws FileNotFoundException { return deleteFolder(folder, false); }
	public static boolean deleteFolder(File folder, boolean onlyContent) 
			throws FileNotFoundException {
		if (!folder.exists()) throw new FileNotFoundException(folder.getAbsolutePath());
		boolean ret = true;
		if (folder.isDirectory()){
			for (File f : folder.listFiles()){
				ret = ret && deleteFolder(f);
			}
		}
		if(onlyContent) return ret;
		else return ret && folder.delete();
	}

	/**
	 * Delete the content of a folder preserving structure
	 * 
	 * @param rootFolder
	 * @throws Exception
	 */
	public static void deleteContentPreserveStructure(File rootFolder) 
	throws Exception {
		File[] root = rootFolder.listFiles();
		for(File f: root) {
			if(f.isDirectory()) deleteContentPreserveStructure(f);
			else f.delete();
		}
	}
	
	/**
	 * By default File#delete fails for non-empty directories, it works like "rm". 
	 * We need something a little more brutual - this does the equivalent of "rm -r"
	 * @param path Root File Path
	 * @return true iff the file and all sub files/directories have been removed
	 * @throws FileNotFoundException
	 */
	@Deprecated
	public static boolean deleteRecursive(File path) 
			throws FileNotFoundException {
		if (!path.exists()) throw new FileNotFoundException(path.getAbsolutePath());
		boolean ret = true;
		if (path.isDirectory()){
			for (File f : path.listFiles()){
				ret = ret && deleteRecursive(f);
			}
		}
		return ret && path.delete();
	}

	/**
	 * Empty a folder content
	 * 
	 * @param folder
	 * @return
	 * @throws FileNotFoundException
	 */
	@Deprecated
	public static boolean emptyFolder(File folder) 
			throws FileNotFoundException {
		if (!folder.exists()) throw new FileNotFoundException(folder.getAbsolutePath());
		boolean ret = true;
		if (folder.isDirectory()){
			for (File f : folder.listFiles()){
				ret = ret && deleteRecursive(f);
			}
		}
		return ret;
	}

	/**
	 * Create folders in a path string
	 * 
	 * @param path
	 * @param deleteIfExist
	 * @return
	 */
	public static boolean createPath(String path, boolean deleteIfExist) {
		File f = new File(path);
		if(!f.exists()) f.mkdirs();
		else {
			if(deleteIfExist) {
				try {
					deleteRecursive(f);
					if(!f.exists()) f.mkdirs();
				} catch (FileNotFoundException e) {
					return false;
				}
			}
		}

		return true;
	}


	/**
	 * Convert string to file
	 * 
	 * @param str
	 * @param fileName
	 */
	public static void stringToFile(String str, String fileName) {
		try {
			File newTextFile = new File(fileName);

			FileWriter fw = new FileWriter(newTextFile);
			fw.write(str);
			fw.close();
		} catch (IOException iox) {
			iox.printStackTrace();
		}
	}

	/**
	 * Get the last folder name from path
	 * 
	 * @param path
	 * @return
	 */
	public static String getFolderFromPath(String path) {
		int lastSlash = path.lastIndexOf("/");
		int lastBackSlash = path.lastIndexOf("\\");
		if(lastSlash == -1 && lastBackSlash == -1) {
			return path;
		}

		int index = (lastSlash >= lastBackSlash) ? lastSlash : lastBackSlash;
		String sub = path;
		if(index < path.length() - 1)
			sub = path.substring(index + 1);

		if(sub.endsWith("/") || sub.endsWith("\\"))
			sub = sub.substring(0, sub.length() - 1);
		return sub;
	}

	/**
	 * Get the parent path of a string path
	 * @param path
	 * @return
	 */
	public static String getParentPath(String path) {
		if(path.endsWith("\\") || path.endsWith("/")) path = path.substring(0, path.length() - 1);
		int lastSlash = path.lastIndexOf("/");
		int lastBackSlash = path.lastIndexOf("\\");
		if(lastSlash == -1 && lastBackSlash == -1) {
			return "";
		}

		int index = (lastSlash >= lastBackSlash) ? lastSlash : lastBackSlash;

		return path.substring(0, index);
	}

	/**
	 * Complete a path with backslash
	 * to fix JFigLocator problem on path concatenation
	 * @param path
	 * @return
	 */
	public static String completePath(String path) {
		if(path == null) return path;
		if(path.endsWith("\\") || path.endsWith("/")) return path;
		return path + "\\";
	}

	public static void main(String[] args){
		// System.out.println(ASFileUtils.postFixFileName(new File("D:\\jsTest\\AC_RunActiveContent.js"), "-min"));
		// System.out.println(ASFileUtils.combinePaths("pippo", "a", "ciao/", "prova"));
		// ASFileUtils.removeLineFromFile("C:\\TEMP\\testFTP\\0061\\201109_09\\indice.xml", "");
		// ASFileUtils.createPath("C:\\TEST\\CEDOLINO\\DOWNLOAD", true);
		System.out.println("1: " + ASFileUtils.getFolderFromPath("C:\\TEST\\CEDOLINO\\DOWNLOAD"));
		System.out.println("2: " + ASFileUtils.getFolderFromPath("DOWNLOAD"));
		System.out.println("3: " + ASFileUtils.getFolderFromPath("DOWNLOAD/"));

		System.out.println("4: " + ASFileUtils.getParentPath("/CFTP01-T/CEDOLINI"));
	}
}
